package cn.enjoy.aspect;

import cn.enjoy.annotation.LogSpel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author K
 * @date 2022/1/20 11:01
 */
@Aspect
@Component
@Slf4j
public class LogSpelAspect {

    private static final ExpressionParser SPEL_PARSER = new SpelExpressionParser();


    @Pointcut("@annotation(cn.enjoy.annotation.LogSpel) || @within(cn.enjoy.annotation.LogSpel)")
    public void logSpelAspect() {
    }

    @Around("logSpelAspect()")
    public Object aroundOperation(JoinPoint joinPoint) throws Throwable {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            LogSpel annotation = method.getAnnotation(LogSpel.class);

            // 获取所有参数
            Object[] args = joinPoint.getArgs();
            // 获取参数别名
            String[] parameterNames = methodSignature.getParameterNames();

            String spelString = annotation.spel();
            String type = annotation.type();
            String description = annotation.description();

            // 参数经过spel表达式转换后值
            String paramStr = parseSpEL(spelString, args, parameterNames);
            log.info("操作类型:{} 描述:{} 参数:{}", type, description, paramStr);

            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();

            log.info("请求地址:" + request.getRequestURL().toString());

        } catch (Exception e) {
            log.info("捕获异常，防止影响业务方法");
        }

        return ((ProceedingJoinPoint) joinPoint).proceed();
    }

    /**
     * @param spELString user.name
     */
    private String parseSpEL(String spELString, Object[] param, String[] parameterNames) {
        try {
            if (StringUtils.isBlank(spELString)) {
                return spELString;
            }
            if (param == null || param.length == 0) {
                return spELString;
            }
            // 解析过后的Spring表达式对象
            Expression expression = SPEL_PARSER.parseExpression(spELString);
            // spring的表达式上下文对象
            EvaluationContext context = new StandardEvaluationContext();
            // 给上下文赋值
            for (int i = 0; i < param.length; i++) {
                //key是参数名，value参数对象
                context.setVariable(parameterNames[i], param[i]);
            }

            return String.valueOf(expression.getValue(context));
        } catch (Exception e) {
            System.out.println("解析异常");
            return spELString;
        }
    }

    /**
     * @param spELString #root[0].name
     */
    private String parseSpEL2(String spELString, Object[] param, String[] parameterNames) {
        try {
            if (StringUtils.isBlank(spELString)) {
                return spELString;
            }
            if (param == null || param.length == 0) {
                return spELString;
            }
            StandardEvaluationContext context = new StandardEvaluationContext(param);
            Expression expression = SPEL_PARSER.parseExpression(spELString);

            return expression.getValue(context, String.class);
        } catch (Exception e) {
            System.out.println("解析异常");
            return spELString;
        }
    }

}
